<?php
/**
 * 树形结构
 * @author yangdongqi <yangdongqi@hayzone.com>
 * @package lib.behaviors.ar.tree
 */
class CTreeBehavior extends CActiveRecordBehavior {
	
	/**
	 * 最大深度
	 * @var int
	 */
	public $maxLayer = 999;
	
	/**
	 * 用户定义的信息
	 * @var array
	 */
	public $userMessages = array();
	
	protected $_messages;
	
	/**
	 * 默认的错误信息
	 * <pre>
	 * array(
	 *  'parentNotExists' => '{attribute}不存在.',
	 *  'parentOverflow' => '{attribute}已经达到最大深度.',
	 * )
	 * </pre>
	 * @return array
	 */
	public function messages() {
		return array(
			'parentNotExists' => '{attribute}不存在.',
			'parentOverflow' => '{attribute}已经达到最大深度.',
		);
	}
	
	public function getMessages() {
		if(!$this->_messages) {
			$this->_messages = array_merge($this->messages(), $this->userMessages);
		}
		return $this->_messages;
	}
	
	/**
	 * 转义消息
	 * @param string $msg
	 * @param array $params
	 * @return string
	 */
	public function tMsg($msg, $params = array()) {
		$message = $this->getMessages();
		$search = array_keys($params);
		$replace = array_values($params);
		return str_replace($search, $replace, $message[$msg]);
	}
	
	
	/**
	 * 寻找父节点
	 * @return CActiveRecord
	 */
	public function getParent() {
		$modelName = get_class($this->owner);
		$model  = new $modelName;
		return $model->findByPk($this->owner->parent_id);
	}
	
	/**
	 * 查找子节点
	 * @return array
	 */
	public function getChildren() {
		return $this->owner->findAllByAttributes(array(
			'parent_id' => $this->owner->id,
		));
	}
	
	/**
	 * 获取祖先节点
	 * @param boolean $hasSelf 是否包含自身节点
	 * @param int $deep 深度
	 * @return array
	 */
	public function getAncestors($hasSelf = true, $deep = 999) {
		$data = array();
		$model = $this->owner;
		$parentModel = $this->getParent();
		if($hasSelf)
			$data[] = $model;
		while($parentModel) {
			array_unshift($data, $parentModel);
			$parentModel = $parentModel->getParent();
		}
		return $data;
	}
	
	/**
	 * 是否含有子节点
	 * @return int 返回子节点数，如果没有，返回0
	 */
	public function getHasChild() {
		return $this->getChildCount()>0;		
	}
	
	/**
	 * 获取子节点数
	 * @return int
	 */
	public function getChildCount() {
		$model = $this->owner;
		return $model->countByAttributes(array(
			'parent_id' => $this->id,
		));
	}
	
	/**
	 * 是否可以添加下级分类
	 * @return boolean
	 */
	public function getSubAddAble() {
		return $this->owner->layer != $this->maxLayer;
	}
	
	/**
	 * 获取后代节点
	 * @param int $deep 深度
	 * @return array
	 */
	public function getDescendants($deep = 999) {
		if($deep == 0) return array();
		$data = $this->getChildren();
		if($data) {
			foreach($data as $one) {
				$data = array_merge($data, $one->getDescendants($deep-1));
			}
		}
		return is_array($data) ? $data : array();
	}
	
	public function getDesWithSelf($deep = 999) {
		return array_merge(array($this->getOwner()), $this->getDescendants($deep));
	}
	
	/**
	 * 查询祖先相邻节点
	 * @param boolean $hasChild 是否包含自身节点
	 * @return array
	 */
	public function getAncestorsSiblings($hasChild = true) {
		$criteria = new CDbCriteria();
		$criteria->condition = '(`layer` = :layer AND parent_id = :pid) OR `layer` < :layer';
		$criteria->addCondition('`layer` <> 0');
		$criteria->params = array(
			':layer' => $this->owner->layer,
			':pid' => $this->owner->parent_id,
		);
		if($hasChild) {
			$criteria->addCondition('`parent_id` = :id', 'OR');
			$criteria->params[':id'] = $this->owner->id;
		}
		$criteria->order = 'layer ASC, id ASC';
		$models = $this->owner->findAll($criteria);
		return $models;
	}
	
	public function validators() {
		return array(
			'parentNotExists',
			'overflow',
		);
	}
	
	/**
	 * 父节点不存在
	 * @return boolean
	 */
	public function parentNotExists() {
		$model = $this->owner;
		if($model->parent_id && !$this->getParent()) {
			$model->addError('parent_id', $this->tMsg('parentNotExists', array(
				'{attribute}' => $model->getAttributeLabel('parent_id')
			)));
			return false;
		}
		return true;
	}
	
	/**
	 * 超过最大深度
	 * @return boolean
	 */
	public function overflow() {
		$model = $this->owner;
		if($this->getParent() && !$this->getParent()->getSubAddAble()) {
			$model->addError('parent_id', $this->tMsg('parentOverflow', array(
				'{attribute}' => $model->getAttributeLabel('parent_id')
			)));
			return false;
		}
		return true;
	}
	
	/**
	 * 验证
	 * (non-PHPdoc)
	 * @see CModelBehavior::beforeValidate()
	 */
	public function beforeValidate($event) {
		$result = true;
		foreach($this->validators() as $v) {
			$result &= $this->{$v}();
		}
		$event->isValid = $result;
	}
	

}

?>